package com.finance.service;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.finance.client.FinanceApiClient;
import com.finance.model.FinanceApiResponse;
import com.finance.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class FinanceDataSyncService {

    private final FinanceApiClient financeApiClient;
    @Autowired
    private ApiLogService apiLogService;
    private final ObjectMapper objectMapper;
    @Autowired
    private FileDownloadUtil fileDownloadUtil;
    @Autowired
    private CsvFileUtil csvFileUtil;
    @Autowired
    private TicketOrderService ticketOrderService;
    @Autowired
    private GoodsOrderService goodsOrderService;
    @Autowired
    private CardIssueService cardIssueService;
    @Autowired
    private CardRechargeService cardRechargeService;
    @Autowired
    private CardConsumptionService cardConsumptionService;
    @Autowired
    private CouponRedeemService couponRedeemService;
    @Autowired
    private GoodsInventoryService goodsInventoryService;
    @Autowired
    private GoodsInOutService goodsInOutService;
    @Autowired
    private SalesRawMaterialService salesRawMaterialService;
    @Autowired
    private SalesItemService salesItemService;
    @Autowired
    private MemberCardRenewalService memberCardRenewalService;
    @Autowired
    private MemberCardRefundService memberCardRefundService;
    @Autowired
    private MemberCardReplacementService memberCardReplacementService;
    @Autowired
    private GoodsOperationDetailService goodsOperationDetailService;
    @Autowired
    private ScreeningDetailService screeningDetailService;
    @Autowired
    private CouponSaleDetailService couponSaleDetailService;
    @Autowired
    private TicketOrderByScreeningDateService ticketOrderByScreeningDateService;
    @Autowired
    private AsyncRequestService asyncRequestService;
    //    private static final String CINEMA_LINK_IDS = "[\"11051461\"]";
//    private static final String CINEMA_LINK_IDS = "[\"12368\",\"15344\",\"12699\",\"12369\",\"12367\",\"16117\",\"16062\",\"12370\",\"12366\",\"16032\",\"15968\"]";
    private static final String CINEMA_LINK_IDS = "[\"12368\"]";
    private static final String AES_PASSWORD = "empc";
    private static final int MAX_RETRY_ATTEMPTS = 3;
    private static final long RETRY_DELAY_MS = 1000; // 1 second delay between retries
    private static final int MAX_CONCURRENT_REQUESTS = 5; // Maximum number of concurrent requests

    public FinanceDataSyncService(FinanceApiClient financeApiClient) {
        this.financeApiClient = financeApiClient;
        this.objectMapper = new ObjectMapper();
    }

    /**
     * 根据指定日期同步所有数据
     *
     * @param searchDate 指定日期
     */
    public void syncDataByDate(String searchDate, boolean forceUpdate) {
        log.info("Starting data sync for date: {}", searchDate);
        int totalDataTypes = DataType.values().length;
        int successCount = 0;
        int failureCount = 0;
        List<String> failedTypes = new ArrayList<>();
        List<CompletableFuture<Boolean>> futures = new ArrayList<>();
        try {
            List<String> cinemaIds = objectMapper.readValue(CINEMA_LINK_IDS, List.class);
//            DataType dataType = DataType.C02;
            for (DataType dataType : DataType.values()) {
                CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        boolean allSuccess = true;
                        for (String cinemaId : cinemaIds) {
                            boolean success = syncSingleDataTypeForCinema(dataType, searchDate, forceUpdate, cinemaId);
                            if (!success) {
                                allSuccess = false;
                                failedTypes.add(dataType.getCode());
                            }
                        }
                        return allSuccess;
                    } catch (Exception e) {
                        log.error("Error processing data type {}: {}", dataType.getCode(), e.getMessage());
                        failedTypes.add(dataType.getCode());
                        return false;
                    }
                });
                futures.add(future);
            }

            // Wait for all futures to complete
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            // Process results
            for (CompletableFuture<Boolean> future : futures) {
                if (future.get()) {
                    successCount++;
                } else {
                    failureCount++;
                }
            }
        } catch (Exception e) {
            log.error("Error during concurrent data sync: {}", e.getMessage());
        }

        // Log final summary
        log.info("Data sync completed. Total types: {}, Success: {}, Failed: {}",
                totalDataTypes, successCount, failureCount);
        if (!failedTypes.isEmpty()) {
            log.warn("Failed data types: {}", String.join(", ", failedTypes));
        }
    }

    /**
     * 同步单个影院的数据
     *
     * @param dataType   数据类型
     * @param searchDate 搜索日期
     * @param cinemaId   影院ID
     * @return 是否同步成功
     */
    private boolean syncSingleDataTypeForCinema(DataType dataType, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("开始同步数据类型: {}, 日期：{}, 影院ID：{}", dataType.getCode(), searchDate, cinemaId);
        boolean success = false;
        int retryCount = 0;
        Exception lastException = null;

        while (retryCount < MAX_RETRY_ATTEMPTS && !success) {
            try {
                if (retryCount > 0) {
                    log.info("Retrying data sync for type: {}, cinema: {}, attempt: {}",
                            dataType.getCode(), cinemaId, retryCount + 1);
                    Thread.sleep(RETRY_DELAY_MS * retryCount);
                }

                String response = financeApiClient.callApi(dataType, searchDate, SearchDateType.DAY,
                        "[\"" + cinemaId + "\"]");
                FinanceApiResponse apiResponse = objectMapper.readValue(response, FinanceApiResponse.class);

                boolean isSuccess = apiResponse.getResultCode() == 200 &&
                        "SUCCESS".equals(apiResponse.getData().getBizCode());

                int status = isSuccess ? 1 : 0;
                int dataCount = 0;
                String errorMessage = !isSuccess ? apiResponse.getData().getBizMsg() : null;

                if (isSuccess) {
                    try {
                        FinanceApiResponse.BizData bizData = apiResponse.getData().getBizData();
                        String async = bizData.getAsync();
                        if ("N".equals(async)) {
                            dataCount = processDownloadUrls(bizData.getDownloadUrlList(),
                                    dataType, searchDate, forceUpdate, cinemaId);
                            log.info("Successfully synced data for type: {}, cinema: {}",
                                    dataType.getCode(), cinemaId);
                            success = true;
                        } else if ("Y".equals(async)) {
                            log.info("异步请求，记录到数据库等待处理");
                            // 创建异步请求记录
                            asyncRequestService.createAsyncRequest(
                                    dataType.getCode(),
                                    searchDate,
                                    cinemaId,
                                    apiResponse.getTraceId()
                            );
                            success = true; // 异步请求也算成功
                        }
                    } catch (Exception e) {
                        log.error("Failed to process download URLs for type: {}, cinema: {}, error: {}",
                                dataType.getCode(), cinemaId, e.getMessage());
                        lastException = e;
                        retryCount++;
                    } finally {
                        apiLogService.logApiCall(
                                "dme.lark.data.finance.getFinancialData",
                                dataType.getCode(),
                                status,
                                dataCount,
                                errorMessage,
                                String.format("dataType=%s, searchDate=%s, cinemaId=%s",
                                        dataType.getCode(), searchDate, cinemaId),
                                response,
                                apiResponse.getTraceId(),
                                retryCount
                        );
                    }
                } else {
                    log.error("API call failed for type: {}, cinema: {}, error: {}",
                            dataType.getCode(), cinemaId, errorMessage);
                    lastException = new RuntimeException("API call failed: " + errorMessage);
                    retryCount++;
                }
            } catch (Exception e) {
                log.error("Error during data sync for type: {}, cinema: {}, attempt: {}, error: {}",
                        dataType.getCode(), cinemaId, retryCount + 1, e.getMessage());
                lastException = e;
                retryCount++;
            }
        }

        if (!success) {
            log.error("Failed to sync data for type: {}, cinema: {} after {} attempts. Last error: {}",
                    dataType.getCode(), cinemaId, MAX_RETRY_ATTEMPTS,
                    lastException != null ? lastException.getMessage() : "Unknown error");
        }

        return success;
    }

    /**
     * 同步指定类型的数据
     *
     * @param dataType 数据类型
     * @param date     指定日期
     * @return 是否同步成功
     */
    public boolean syncDataByType(DataType dataType, LocalDate date, boolean forceUpdate) {
        String searchDate = date.format(DateTimeFormatter.ISO_DATE);
        log.info("Starting data sync for type: {}, date: {}", dataType.getCode(), searchDate);
        try {
            List<String> cinemaIds = objectMapper.readValue(CINEMA_LINK_IDS, List.class);
            List<CompletableFuture<Boolean>> futures = new ArrayList<>();

            for (String cinemaId : cinemaIds) {
                CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        return syncSingleDataTypeForCinema(dataType, searchDate, forceUpdate, cinemaId);
                    } catch (Exception e) {
                        log.error("Error processing cinema ID {}: {}", cinemaId, e.getMessage());
                        return false;
                    }
                });
                futures.add(future);
            }

            // Wait for all futures to complete
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

            // Check if all operations were successful
            return futures.stream().allMatch(future -> {
                try {
                    return future.get();
                } catch (Exception e) {
                    log.error("Error getting future result: {}", e.getMessage());
                    return false;
                }
            });

        } catch (Exception e) {
            log.error("Error during concurrent data sync: {}", e.getMessage());
            return false;
        }
    }

    private int processDownloadUrls(List<String> downloadUrlList, DataType dataType, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("开始下载数据:{}, {},日期：{}", JSONObject.toJSONString(downloadUrlList), dataType.getCode(), searchDate);
        if (downloadUrlList == null || downloadUrlList.isEmpty()) {
            log.warn("No download URLs found for data type: {}", dataType.getCode());
            return 0;
        }
        String baseDir = "./downloads/" + dataType.getCode() + "/" + cinemaId;

        for (String encryptedUrl : downloadUrlList) {
            String decryptedUrl = AESUtil.decrypt(encryptedUrl, AES_PASSWORD);
            if (decryptedUrl != null) {
                // 生成目标文件路径C01-2025-01-29-06_00_00-2025-01-30-05_59_59.csv
                // 将字符串转换为 LocalDate 对象
                LocalDate localDate = LocalDate.parse(searchDate);
                // 获取第二天的日期
                LocalDate nextDay = localDate.plusDays(1);
                // 将第二天的日期转换为字符串
                String tomorrow = nextDay.format(DateTimeFormatter.ISO_DATE);
                String fileName = dataType.getCode() + "-" + searchDate + "-06_00_00-" + tomorrow + "-05_59_59" + ".csv";
                String targetPath = baseDir + "/" + fileName;
                log.info("生成文件至:code:{}, date:{}, path:{}", dataType.getCode(), searchDate, targetPath);

                // 下载文件
                String downloadedPath = fileDownloadUtil.downloadFile(decryptedUrl, targetPath);
                log.info("Successfully downloaded file to: {}", downloadedPath);

                // 读取CSV文件内容
                List<Map<String, String>> records = csvFileUtil.readCsvFile(downloadedPath);
                log.info("读取csv文件信息:code:{}, date:{}, size:{}", dataType.getCode(), searchDate, records.size());

                // 根据不同的数据类型处理CSV数据
                processCsvData(records, dataType, searchDate, forceUpdate, cinemaId);

                return records.size();
            } else {
                log.error("Failed to decrypt download URL for data type: {}", dataType.getCode());
            }
        }
        return 0;
    }

    /**
     * 处理CSV数据
     *
     * @param records  CSV数据记录
     * @param dataType 数据类型
     */
    private void processCsvData(List<Map<String, String>> records, DataType dataType, String searchDate, boolean forceUpdate, String cinemaId) {
        // 根据不同的数据类型处理数据
        switch (dataType) {
            case C01:
                processC01Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C02:
                processC02Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C03:
                processC03Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C04:
                processC04Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C05:
                processC05Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C06:
                processC06Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C07:
                processC07Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C08:
                processC08Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C09:
                processC09Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C10:
                processC10Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C11:
                processC11Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C12:
                processC12Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C14:
                processC14Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C15:
                processC15Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C18:
                processC18Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C19:
                processC19Data(records, searchDate, forceUpdate, cinemaId);
                break;
            case C20:
                processC20Data(records, searchDate, forceUpdate, cinemaId);
                break;
            // 添加其他数据类型的处理
            default:
                log.warn("Unsupported data type: {}", dataType.getCode());
        }
    }

    /**
     * 处理C01类型数据
     */
    private void processC01Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C01 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C01数据为空");
                return;
            }
            int total = ticketOrderService.saveTicketOrder(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C01, size:{}", total);
        } catch (Exception e) {
            log.error("处理C01数据失败", e);
            throw new RuntimeException("处理C01数据失败", e);
        }
    }

    /**
     * 处理C02类型数据
     */
    private void processC02Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C02 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C02数据为空");
                return;
            }
            int total = goodsOrderService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C02, size:{}", total);
        } catch (Exception e) {
            log.error("处理C02数据失败", e);
            throw new RuntimeException("处理C02数据失败", e);
        }
    }

    /**
     * 处理C03类型数据
     */
    private void processC03Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C03 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C03数据为空");
                return;
            }
            int total = cardIssueService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C03, size:{}", total);
        } catch (Exception e) {
            log.error("处理C03数据失败", e);
            throw new RuntimeException("处理C03数据失败", e);
        }
    }

    /**
     * 处理C04类型数据
     */
    private void processC04Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C04 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C04数据为空");
                return;
            }
            int total = cardRechargeService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C04, size:{}", total);
        } catch (Exception e) {
            log.error("处理C04数据失败", e);
            throw new RuntimeException("处理C04数据失败", e);
        }
    }

    /**
     * 处理C05类型数据
     */
    private void processC05Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C05 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C05数据为空");
                return;
            }
            int total = cardConsumptionService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C05, size:{}", total);
        } catch (Exception e) {
            log.error("处理C05数据失败", e);
            throw new RuntimeException("处理C05数据失败", e);
        }
    }

    /**
     * 处理C06类型数据
     */
    private void processC06Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C06 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C06数据为空");
                return;
            }
            int total = couponRedeemService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C06, size:{}", total);
        } catch (Exception e) {
            log.error("处理C06数据失败", e);
            throw new RuntimeException("处理C06数据失败", e);
        }
    }

    /**
     * 处理C07类型数据
     */
    private void processC07Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C07 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C07数据为空");
                return;
            }
            int total = goodsInventoryService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C07, size:{}", total);
        } catch (Exception e) {
            log.error("处理C07数据失败", e);
            throw new RuntimeException("处理C07数据失败", e);
        }
    }

    /**
     * 处理C08类型数据
     */
    private void processC08Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C08 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C08数据为空");
                return;
            }
            int total = goodsInOutService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C08, size:{}", total);
        } catch (Exception e) {
            log.error("处理C08数据失败", e);
            throw new RuntimeException("处理C08数据失败", e);
        }
    }

    /**
     * 处理C09类型数据
     */
    private void processC09Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C09 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C09数据为空");
                return;
            }
            int total = salesRawMaterialService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C09, size:{}", total);
        } catch (Exception e) {
            log.error("处理C09数据失败", e);
            throw new RuntimeException("处理C09数据失败", e);
        }
    }

    /**
     * 处理C10类型数据
     */
    private void processC10Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C10 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C10数据为空");
                return;
            }
            int total = salesItemService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C10, size:{}", total);
        } catch (Exception e) {
            log.error("处理C10数据失败", e);
            throw new RuntimeException("处理C10数据失败", e);
        }
    }

    /**
     * 处理C11类型数据
     */
    private void processC11Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C11 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C11数据为空");
                return;
            }
            int total = memberCardRenewalService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C11, size:{}", total);
        } catch (Exception e) {
            log.error("处理C11数据失败", e);
            throw new RuntimeException("处理C11数据失败", e);
        }
    }

    /**
     * 处理C12类型数据
     */
    private void processC12Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C12 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C12数据为空");
                return;
            }
            int total = memberCardRefundService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C12, size:{}", total);
        } catch (Exception e) {
            log.error("处理C12数据失败", e);
            throw new RuntimeException("处理C12数据失败", e);
        }
    }

    /**
     * 处理C14类型数据
     */
    private void processC14Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C14 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C14数据为空");
                return;
            }
            int total = memberCardReplacementService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C14, size:{}", total);
        } catch (Exception e) {
            log.error("处理C14数据失败", e);
            throw new RuntimeException("处理C14数据失败", e);
        }
    }

    /**
     * 处理C15类型数据
     */
    private void processC15Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C15 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C15数据为空");
                return;
            }
            int total = goodsOperationDetailService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C15, size:{}", total);
        } catch (Exception e) {
            log.error("处理C15数据失败", e);
            throw new RuntimeException("处理C15数据失败", e);
        }
    }

    /**
     * 处理C18类型数据
     */
    private void processC18Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C18 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C18数据为空");
                return;
            }
            int total = screeningDetailService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C18, size:{}", total);
        } catch (Exception e) {
            log.error("处理C18数据失败", e);
            throw new RuntimeException("处理C18数据失败", e);
        }
    }

    /**
     * 处理C19类型数据
     */
    private void processC19Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C19 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C19数据为空");
                return;
            }
            int total = couponSaleDetailService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C19, size:{}", total);
        } catch (Exception e) {
            log.error("处理C19数据失败", e);
            throw new RuntimeException("处理C19数据失败", e);
        }
    }

    /**
     * 处理C20类型数据
     */
    private void processC20Data(List<Map<String, String>> records, String searchDate, boolean forceUpdate, String cinemaId) {
        log.info("Processing C20 data, total records: {}", records.size());
        try {
            if (records.isEmpty()) {
                log.warn("C20数据为空");
                return;
            }
            int total = ticketOrderByScreeningDateService.processOrders(records, searchDate, forceUpdate, cinemaId);
            log.info("成功入库条数:C20, size:{}", total);
        } catch (Exception e) {
            log.error("处理C20数据失败", e);
            throw new RuntimeException("处理C20数据失败", e);
        }
    }
}